package org.cosmos.modules.utils.file;

import java.awt.Dimension;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

/**
 * @author fisher
 * @description 图像处理类,用于验证码
 */
public class ImageUtils {
    public static final int IMAGE_UNKNOWN = -1;
    public static final int IMAGE_JPEG = 0;
    public static final int IMAGE_PNG = 1;
    public static final int IMAGE_GIF = 2;

    public static BufferedImage resizeImage(String imgName,
                                            int type,
                                            int maxWidth,
                                            int maxHeight) throws IOException {
        return resizeImage(ImageIO.read(new File(imgName)), type, maxWidth, maxHeight);
    }

    public static BufferedImage resizeImage(BufferedImage image,
                                            int type,
                                            int maxWidth,
                                            int maxHeight) {
        Dimension largestDimension = new Dimension(maxWidth, maxHeight);

        int imageWidth = image.getWidth(null);
        int imageHeight = image.getHeight(null);

        float aspectRatio = imageWidth / imageHeight;

        if ((imageWidth > maxWidth) || (imageHeight > maxHeight)) {
            if (largestDimension.width / largestDimension.height > aspectRatio) {
                largestDimension.width = (int) Math.ceil(largestDimension.height * aspectRatio);
            } else {
                largestDimension.height = (int) Math.ceil(largestDimension.width / aspectRatio);
            }

            imageWidth = largestDimension.width;
            imageHeight = largestDimension.height;
        }

        return createHeadlessSmoothBufferedImage(image, type, imageWidth, imageHeight);
    }

    public static boolean saveImage(BufferedImage image,
                                    String toFileName,
                                    int type) throws IOException {
        return ImageIO.write(image, type == 0 ? "jpg" : "png", new File(toFileName));
    }

    public static void saveCompressedImage(BufferedImage image,
                                           String toFileName,
                                           int type) throws IOException {
        if (type == 1) {
            throw new UnsupportedOperationException("PNG compression not implemented");
        }

        Iterator<?> iter = ImageIO.getImageWritersByFormatName("jpg");

        ImageWriter writer = (ImageWriter) iter.next();

        ImageOutputStream ios = ImageIO.createImageOutputStream(new File(toFileName));
        writer.setOutput(ios);

        ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());

        iwparam.setCompressionMode(2);
        iwparam.setCompressionQuality(0.7F);

        writer.write(null, new IIOImage(image, null, null), iwparam);

        ios.flush();
        writer.dispose();
        ios.close();
    }

    public static BufferedImage createHeadlessBufferedImage(BufferedImage image,
                                                            int type,
                                                            int width,
                                                            int height) {
        if ((type == 1) && (hasAlpha(image))) {
            type = 2;
        } else {
            type = 1;
        }

        BufferedImage bi = new BufferedImage(width, height, type);

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                bi.setRGB(x, y, image.getRGB(x * image.getWidth() / width, y * image.getHeight()
                        / height));
            }
        }

        return bi;
    }

    public static BufferedImage createHeadlessSmoothBufferedImage(BufferedImage source,
                                                                  int type,
                                                                  int width,
                                                                  int height) {
        if ((type == 1) && (hasAlpha(source))) {
            type = 2;
        } else {
            type = 1;
        }

        BufferedImage dest = new BufferedImage(width, height, type);

        double scalex = width / source.getWidth();
        double scaley = height / source.getHeight();

        for (int y = 0; y < height; y++) {
            int sourcey = y * source.getHeight() / dest.getHeight();
            double ydiff = scale(y, scaley) - sourcey;

            for (int x = 0; x < width; x++) {
                int sourcex = x * source.getWidth() / dest.getWidth();
                double xdiff = scale(x, scalex) - sourcex;

                int x1 = Math.min(source.getWidth() - 1, sourcex + 1);
                int y1 = Math.min(source.getHeight() - 1, sourcey + 1);

                int rgb1 = getRGBInterpolation(source.getRGB(sourcex, sourcey),
                                               source.getRGB(x1, sourcey),
                                               xdiff);
                int rgb2 = getRGBInterpolation(source.getRGB(sourcex, y1),
                                               source.getRGB(x1, y1),
                                               xdiff);

                int rgb = getRGBInterpolation(rgb1, rgb2, ydiff);

                dest.setRGB(x, y, rgb);
            }
        }

        return dest;
    }

    private static double scale(int point,
                                double scale) {
        return point / scale;
    }

    private static int getRGBInterpolation(int value1,
                                           int value2,
                                           double distance) {
        int alpha1 = (value1 & 0xFF000000) >>> 24;
        int red1 = (value1 & 0xFF0000) >> 16;
        int green1 = (value1 & 0xFF00) >> 8;
        int blue1 = value1 & 0xFF;

        int alpha2 = (value2 & 0xFF000000) >>> 24;
        int red2 = (value2 & 0xFF0000) >> 16;
        int green2 = (value2 & 0xFF00) >> 8;
        int blue2 = value2 & 0xFF;

        int rgb = (int) (alpha1 * (1.0D - distance) + alpha2 * distance) << 24
                | (int) (red1 * (1.0D - distance) + red2 * distance) << 16
                | (int) (green1 * (1.0D - distance) + green2 * distance) << 8
                | (int) (blue1 * (1.0D - distance) + blue2 * distance);

        return rgb;
    }

    public static boolean hasAlpha(Image image) {
        try {
            PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
            pg.grabPixels();

            return pg.getColorModel().hasAlpha();
        } catch (InterruptedException e) {
        }
        return false;
    }
}